/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

#include <stdlib.h>

#include "Tesselator.h"


using namespace Scenic::Renderer;


//
// class TessVertexBuffer
//

TessVertexBuffer::TessVertexBuffer()
{
	tessBlocks = 0;
	numTessVertex = 0;
	vertexCount = 0;
}

TessVertexBuffer::~TessVertexBuffer()
{
	free();
}

TessVertex *TessVertexBuffer::newVertex()
{
	if(tessBlocks == NULL || numTessVertex >= TESS_BLOCK_SIZE)
	{
		TessBlock *b = (TessBlock*)malloc(sizeof(TessBlock));
		
		b->next = tessBlocks;
		tessBlocks = b;
		numTessVertex = 0;
	}

	TessVertex *v = &tessBlocks->vertices[numTessVertex++];

	v->id = vertexCount++;

	return v;
}

void TessVertexBuffer::free()
{
	while(tessBlocks) {
		struct TessBlock *next = tessBlocks->next;

		::free(tessBlocks);
		tessBlocks = next;
	}
	numTessVertex = 0;
	vertexCount = 0;
}

//
// class Tesselator
//

Tesselator::~Tesselator()
{
}

//
// class GLUTesselator
//

static ResourcePool tesselatorPool("Tesselator");


static void __stdcall tessBegin(GLenum mode)
{
}

static void __stdcall tessEnd()
{
}

static void __stdcall tessEdgeFlag(GLboolean flag, GLUTesselator *tesselator)
{
	tesselator->edgeFlag(flag ? true : false);
}

static void __stdcall tessCombineFunc(double coords[3], struct TessVertex *d[4], float w[4], struct TessVertex **dataOut,
							GLUTesselator *tesselator)
{
	tesselator->combineFunc(coords, d, w, dataOut);
}

static void __stdcall tessVertexFunc(struct TessVertex *data, GLUTesselator *tesselator)
{
	tesselator->vertexFunc(data);
}

GLUTesselator::GLUTesselator()
{
	tessObj = gluNewTess();

	gluTessNormal(tessObj, 0, 0, 1);
	gluTessProperty(tessObj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
	gluTessCallback(tessObj, GLU_TESS_BEGIN, (GLvoid (__stdcall *)())tessBegin);
	gluTessCallback(tessObj, GLU_TESS_VERTEX_DATA, (GLvoid (__stdcall *)())tessVertexFunc); 
	gluTessCallback(tessObj, GLU_TESS_END, (GLvoid (__stdcall *)())tessEnd);
	gluTessCallback(tessObj, GLU_TESS_COMBINE_DATA, (GLvoid (__stdcall *)())tessCombineFunc);
	gluTessCallback(tessObj, GLU_TESS_EDGE_FLAG_DATA, (GLvoid (__stdcall *)())tessEdgeFlag);
}

GLUTesselator::~GLUTesselator()
{
	gluDeleteTess(tessObj);
}

/*
Tesselator *Tesselator::get(int id)
{
	return (Tesselator*)tesselatorPool.get(id);
}
*/
void GLUTesselator::begin(TriangleSink *sink)
{
	this->sink = sink;
	vertexCount = 0;
	gluTessBeginPolygon(tessObj, this);
	sink->begin();
}

void GLUTesselator::end()
{
	gluTessEndPolygon(tessObj);
	buffer.free();
	sink->end();
}

void GLUTesselator::beginContour()
{
	gluTessBeginContour(tessObj);
}

void GLUTesselator::endContour()
{
	gluTessEndContour(tessObj);
}

void GLUTesselator::vertex(double x, double y)
{
	struct TessVertex *v = buffer.newVertex();
	
	v->p[0] = x;
	v->p[1] = y;
	v->p[2] = 0.0;
	gluTessVertex(tessObj, v->p, v);	
	
	sink->addVertex(x, y);
}

void GLUTesselator::combineFunc(double coords[3], struct TessVertex *d[4], 
										   float w[4], struct TessVertex **dataOut)
{
	struct TessVertex *v = buffer.newVertex();
	
	v->p[0] = coords[0];
	v->p[1] = coords[1];
	v->p[2] = coords[2];
		
	*dataOut = v;
	sink->addVertex(coords[0], coords[1]);
}

void GLUTesselator::edgeFlag(bool edge)
{
	this->edge = edge;
}

void GLUTesselator::vertexFunc(struct TessVertex *data)
{
	edges[vertexCount] = edge;
	vertices[vertexCount++] = data->id;

	if(vertexCount == 3)
	{
		sink->addTriangle(vertices[0], edges[0], vertices[1], edges[1], vertices[2], edges[2]);
		vertexCount = 0;
	}
}

//
// class ConvexTesselator
//

ConvexTesselator::ConvexTesselator()
{
}

ConvexTesselator::~ConvexTesselator()
{
}

void ConvexTesselator::begin(TriangleSink *sink)
{
	this->sink = sink;
	vertexCount = 0;
	sink->begin();
}

void ConvexTesselator::end()
{
	sink->end();
}

void ConvexTesselator::beginContour()
{
	contourLength = 0;
}

void ConvexTesselator::endContour()
{
	int base = vertexCount - contourLength;
	int i;

	for(i = 2; i < contourLength; i++)
		sink->addTriangle(base, i == 2, base + i - 1, true, base + i, i == contourLength - 1);
}

void ConvexTesselator::vertex(double x, double y)
{
	sink->addVertex(x, y);
	contourLength++;
	vertexCount++;
}